package com.roamer.audition.web.security.filter;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.roamer.util.Assert;
import com.roamer.util.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 支持JSON方式登陆验证
 *
 * @author roamer
 * @version v1.0
 * @date 2020/3/9 15:48
 */
public class UsernamePasswordAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    private static final String PRINCIPAL_ATTRIBUTE_NAME = "username";
    private static final String CREDENTIALS_ATTRIBUTE_NAME = "password";
    private static final ObjectMapper jsonObj = new ObjectMapper();

    public UsernamePasswordAuthenticationProcessingFilter() {
        //拦截url为 "/login" 的POST请求
        super(new AntPathRequestMatcher("/login", "POST"));
    }

    @Override
    public void afterPropertiesSet() {
        Assert.nonNull(getAuthenticationManager(), "authenticationManager must be specified");
        Assert.nonNull(getSuccessHandler(), "AuthenticationSuccessHandler must be specified");
        Assert.nonNull(getFailureHandler(), "AuthenticationFailureHandler must be specified");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        //从Body中获取username和password
        String body = StreamUtils.copyToString(httpServletRequest.getInputStream(), StandardCharsets.UTF_8);
        String username = null, password = null;
        if (StringUtils.hasText(body)) {
            JsonNode jsonNode = jsonObj.readValue(body, JsonNode.class);
            if (jsonNode.hasNonNull(PRINCIPAL_ATTRIBUTE_NAME)) {
                username = jsonNode.get(PRINCIPAL_ATTRIBUTE_NAME).asText();
            }
            if (jsonNode.hasNonNull(CREDENTIALS_ATTRIBUTE_NAME)) {
                password = jsonNode.get(CREDENTIALS_ATTRIBUTE_NAME).asText();
            }
        }
        username = username == null ? "" : username.trim();
        password = password == null ? "" : password;
        // 封装到token中提交
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);
        // 1。交给AuthenticationManager.authenticate(authentication)（默认ProviderManager）处理
        // 2。AuthenticationManager 再委托 AuthenticationProvider.authenticate(authentication) 处理
        //   （通过调用AuthenticationProvider的supports来选取）UsernamePasswordAuthenticationToken 类型凭证使用
        //    DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
        // 3。AbstractUserDetailsAuthenticationProvider.authenticate(authentication) 核心流程
        //   -- 获取用户信息 DaoAuthenticationProvider.retrieveUser(username,authentication) 委托
        //      UserDetailsService.loadUserByUsername(username)获取系统中存储的用户信息(需要实现UserDetailsService)
        //   -- 对用户信息做了过期和锁定等校验后交给additionalAuthenticationChecks()和用户提交的信息做比对。
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}
